package com.cgy.utils.base.spi;

import com.cgy.utils.base.strings.StringHelper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import static com.cgy.utils.base.constant.Strings.UTF_8;

/**
 * spi 工具类
 *
 * @author CH
 */
@Slf4j
public class SpiHelper<T> {

    /**
     * 当前基础包
     */
    private static final String BASE_PACKAGE = StringHelper.lastEnd(SpiHelper.class.getPackage().getName());

    /**
     * 存放不同类型的loader
     */
    private static final ConcurrentHashMap<Class<?>, SpiHelper<?>> loaderMap = new ConcurrentHashMap<>();


    private static ConcurrentHashMap<String, Object> CLASS_CACHE = new ConcurrentHashMap<>();

    /**
     *加载不同的所有的实现的类定义
     */
    private final ConcurrentHashMap<String, Class<?>> extensionClassesMap = new ConcurrentHashMap<String, Class<?>>();
    private final ConcurrentHashMap<String, List> extensionClassesList = new ConcurrentHashMap<>();


    private static final String SERVICES_DIRECTORY = "META-INF" + File.separator + "services" + File.separator;
    private String order;

    private Class<?> extensionType;

    private ClassLoader classLoader;

    /**
     *
     * @param extensionType
     * @throws Exception
     */
    private SpiHelper(Class<?> extensionType, SpiConfig spiConfig) throws Exception {
        this.extensionType = extensionType;
        if(null != spiConfig) {
            if(null != spiConfig.classLoader()) {
                this.classLoader = spiConfig.classLoader();
            }
            this.order = StringUtils.defaultString(spiConfig.order(), "time");
        }
        this.loadExtensionClasses(extensionType);
    }

    /**
     * 获取默认实例
     * @return
     * @throws Exception
     */
    public T getDefaultSpiService() {
        Spi spi = extensionType.getAnnotation(Spi.class);
        try {
            return this.getSpiService(spi.value());
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取所有实例
     */
    public Map<String, T> getAllMapSpiService() {
        Map<String, T> result = Maps.newHashMap();
        for (Map.Entry<String, Class<?>> entry : extensionClassesMap.entrySet()) {
            Class<?> value = entry.getValue();
            try {
                T o = (T) value.newInstance();
                result.put(entry.getKey(), o);
            } catch (Exception e) {
                continue;
            }
        }
        return result;
    }

    /**
     * 获取所有实例
     */
    public List<T> getAllSpiService() {
        List<Class<T>> list = extensionClassesList.get(extensionType.getName());
        if(null != list) {
            List<T> result = new ArrayList<>(list.size());
            for (Class<T> tClass : list) {
                try {
                    T t = tClass.newInstance();
                    result.add(t);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            return result;
        } else {
            return (List<T>) CLASS_CACHE.get(extensionType.getName());
        }
    }
    /**
     * 获取默认实例
     * @return
     */
    public T getNotNullSpiService() {
        if(!extensionClassesMap.isEmpty()) {
            Class<?> aClass = extensionClassesMap.elements().nextElement();
            try {
                return (T) aClass.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    /**
     * 获取示例
     *
     * @param name
     * @return
     */
    public T getSpiService(final String name) {
        if (StringUtils.isBlank(name)) {
            throw new IllegalArgumentException("name is null!");
        }
        //从map中取实例如果取不到 就创建病存放到map中
        T t = (T) CLASS_CACHE.get(name);
        if (t == null) {
            Class<?> clazz = extensionClassesMap.get(name);
            if (clazz == null) {
                log.error("name:[" + name + "] not defination in file(" + SERVICES_DIRECTORY + extensionType.getName() + ")");
                t = null;
            }
            if(null != clazz) {
                try {
                    t = (T) clazz.newInstance();
                    CLASS_CACHE.put(name, t);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return t;
    }


    /**
     * Description: 加载不同的类定义. <br/>
     *
     * @param extensionType 扩展类型
     * @throws Exception
     */
    private void loadExtensionClasses(Class<?> extensionType) throws Exception {
        //文件名 也就是接口名
        String fileName = SERVICES_DIRECTORY + extensionType.getName();
        ClassLoader classLoader = null != this.classLoader ? this.classLoader : ClassLoader.getSystemClassLoader();
        Enumeration<URL> urls = classLoader.getResources(fileName);

        if(urls == null || urls.hasMoreElements() || null == classLoader) {
            classLoader = SpiHelper.class.getClassLoader();
            urls = classLoader.getResources(fileName);
        }

        List<Object> listSubType = Lists.newArrayList();
        List<Object> listSubKeyValueType = Lists.newArrayList();


        if (urls != null && urls.hasMoreElements()) {
            while (urls.hasMoreElements()) {
                java.net.URL url = urls.nextElement();
                BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8));
                String line = null;
                try {
                    while ((line = reader.readLine()) != null) {
                        if (line == null || line.trim().length() == 0 || line.contains("#")) {
                            continue;
                        }
                        //读取文件一行定义并处理
                        line = line.trim();
                        String[] defArray = line.split("=");
                        if (defArray.length != 2) {
                            //不拆分文件当前为实现类
                            Class<?> aClass = classLoader.loadClass(line);
                            Object o = aClass.newInstance();
                            listSubType.add(o);
                        } else {
                            //拆分文件，第一部分是名字，第二部分是类全名
                            String name = defArray[0], className = defArray[1];
                            Class<?> clazz = Class.forName(className, true, classLoader);
                            //判断文件定义的类，是不是加载接口的子类
                            if (!extensionType.isAssignableFrom(clazz)) {
                                throw new IllegalStateException("class line defination [" + className + "] not an subType of(" + extensionType.getName() + ")");
                            }
                            extensionClassesMap.put(name, clazz);
                            listSubKeyValueType.add(clazz);
                        }
                    }
                } finally {
                    reader.close();
                }

            }
        }
        if(listSubType.size() > 0) {
            CLASS_CACHE.put(extensionType.getName(), listSubType);
        }
        if(listSubKeyValueType.size() > 0) {
            extensionClassesList.put(extensionType.getName(), listSubKeyValueType);
        }
    }

    /**
     * 获取非base服务
     *
     * @param clz
     * @param <T>
     * @return
     */
    public static <T> List<T> getExtensionService(Class<T> clz, ClassLoader... classLoader) {
        ClassLoader classLoader1 = null;
        if(null != classLoader && classLoader.length > 0) {
            classLoader1 = classLoader[0];
        } else {
            classLoader1 = ClassLoader.getSystemClassLoader();
        }
        List<T> sips = getSpis(clz, classLoader1);

        List<T> list = new ArrayList<>();
        if(null != sips && !sips.isEmpty()) {
            for (T spi : sips) {
                Package aPackage = spi.getClass().getPackage();
                if(!aPackage.getName().startsWith(BASE_PACKAGE)) {
                    list.add(spi);
                }
            }
        }
        return list;
    }
    /**
     * 获取所有服务
     *
     * @param clz
     * @param <T>
     * @return
     */
    public static <T> List<T> getSpis(Class<T> clz) {
        return getSpis(clz, ClassLoader.getSystemClassLoader());
    }
    /**
     * 获取所有服务
     *
     * @param clz
     * @param classLoader
     * @param <T>
     * @return
     */
    public static <T> List<T> getSpis(Class<T> clz, ClassLoader classLoader) {
        List<T> list = new ArrayList<>();
        if (null != clz) {
            String name = clz.getName();
            list = (List<T>) CLASS_CACHE.get(name);
            if (null == list) {
                list = new ArrayList<>();
                ServiceLoader<T> serviceLoader;
                if (null == classLoader) {
                    serviceLoader = ServiceLoader.load(clz);
                } else {
                    serviceLoader = ServiceLoader.load(clz, classLoader);
                }

                if (null != serviceLoader) {
                    Iterator<T> iterator = serviceLoader.iterator();
                    try {
                        while (iterator.hasNext()) {
                            T next = iterator.next();
                            if(null != next) {
                                list.add(next);
                            }
                        }
                    } catch (Exception e) {
                        log.warn("Ignore all error services");
                    }
                }
                CLASS_CACHE.put(name, list);
            }
        }
        return list;
    }

    /**
     * 获取一个服务
     *
     * @param clz
     * @param <T>
     * @return
     */
    public static <T> T getSpi(Class<T> clz, ClassLoader... classLoaders) {
        ClassLoader classLoader = null;
        if(null != classLoaders && classLoaders.length > 0) {
            classLoader = classLoaders[0];
        } else {
            classLoader = ClassLoader.getSystemClassLoader();
        }

        List<T> sips = getSpis(clz, classLoader);
        return sips.isEmpty() ? null : sips.get(sips.size() - 1);
    }

    /**
     * Description: . <br/>
     *
     * @param extensionType
     * @return
     * @throws Exception
     */
    public static <T> SpiHelper getServiceLoader(Class<?> extensionType) {
        return getServiceLoader(extensionType, null);
    }
    /**
     * Description: . <br/>
     *
     * @param extensionType
     * @return
     * @throws Exception
     */
    public static <T> SpiHelper getServiceLoader(Class<?> extensionType, SpiConfig spiConfig) {
        if (extensionType == null) {
            throw new IllegalArgumentException("extensionType is null!");
        }
        if (null != spiConfig && spiConfig.intercept() && !extensionType.isAnnotationPresent(Spi.class)) {
            throw new IllegalArgumentException("extensionType (" + extensionType + ")Invalid extension,because: No annotations (@" + Spi.class.getSimpleName() + ")!");
        }

        SpiHelper<?> spiHelper = loaderMap.get(extensionType);
        if (null == spiHelper) {
            try {
                spiHelper = new SpiHelper<T>(extensionType, spiConfig);
            } catch (Exception e) {
                e.printStackTrace();
            }
            loaderMap.put(extensionType, spiHelper);
        }
        return spiHelper;
    }


}
